﻿#region 文件信息

/*----------------------------------------------------------------
//
// 文件名称：
// 文件功能描述：
// 设计要求：
//
// 文 件 名：    RpcService
// 创建者：      杨程
// 创建日期：	    2022/12/20 21:25:03

//----------------------------------------------------------------*/

#endregion

using Newtonsoft.Json;
using TouchSocket.Core;
using TouchSocket.Rpc;
using TouchSocket.Rpc.TouchRpc;
using TouchSocket.Sockets;
using Vampirewal.Core;

namespace Vampirewal.Admin.Client.ClientRpcServer;

/// <summary>
/// RPC客户端服务
/// </summary>
public interface IRpcService
{
    TcpTouchRpcClient client { get; }

    /// <summary>
    /// 直接请求API
    /// </summary>
    /// <param name="MethodName">方法名</param>
    /// <param name="option">默认InvokeOption.WaitInvoke</param>
    /// <param name="Error">报错之后执行方法</param>
    /// <param name="parameters">参数</param>
    void CallApi(string MethodName, InvokeOption option, Action<Exception> Error, params object[] parameters);

    /// <summary>
    /// 直接请求Api
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="MethodName">方法名</param>
    /// <param name="success">成功之后执行方法</param>
    /// <param name="Error">报错之后执行方法</param>
    /// <param name="parameters"></param>
    void CallApi<T>(string MethodName, InvokeOption option, Action<T> success, Action<Exception>? Error = null, params object[] parameters);

    /// <summary>
    /// 直接请求Api通过把类转成json(无返回值)
    /// </summary>
    /// <typeparam name="T">类型</typeparam>
    /// <param name="MethodName">方法名</param>
    /// <param name="option">默认InvokeOption.WaitInvoke</param>
    /// <param name="Error">报错之后执行方法</param>
    /// <param name="t">参数</param>
    void CallApiByJson<T>(string MethodName, InvokeOption option, Action<Exception> Error, T? t = null) where T : class;

    /// <summary>
    /// 直接请求Api通过把类转成json(有返回值)
    /// </summary>
    /// <typeparam name="T">类型</typeparam>
    /// <param name="MethodName">方法名</param>
    /// <param name="option">默认InvokeOption.WaitInvoke</param>
    /// <param name="success">成功之后执行方法</param>
    /// <param name="Error">报错之后执行方法</param>
    /// <param name="t">参数</param>
    void CallApiByJson<T>(string MethodName, InvokeOption option, Action<T> success, Action<Exception>? Error = null, T? t = null) where T : class;

    /// <summary>
    /// 使用ResponseBasic包裹的Api请求
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="MethodName">方法名</param>
    /// <param name="success">成功之后执行方法</param>
    /// <param name="Error">报错之后执行方法</param>
    /// <param name="parameters">参数</param>
    void ResponseApi<T>(string MethodName, Action<T> success, Action<Exception>? Error = null, params object[] parameters);

    /// <summary>
    /// 使用ResponseBasic包裹的Api请求
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="obj">需要传递的类</param>
    /// <param name="MethodName">方法名</param>
    /// <param name="success">成功之后执行方法</param>
    /// <param name="Error">报错之后执行方法</param>
    void ResponseApiByJson<T>(object obj, string MethodName, Action<T> success, Action<Exception>? Error = null);

    /// <summary>
    /// 上传文件
    /// </summary>
    /// <param name="BillId"></param>
    /// <param name="FilePath"></param>
    /// <returns></returns>
    Task<bool> FileUpload(string BillId, string FilePath, string FileName);

    /// <summary>
    /// 下载附件
    /// </summary>
    /// <param name="FileServerName"></param>
    /// <returns></returns>
    Task<bool> FileDownload(string FileServerName, string SavePath = "");
}

/// <summary>
/// RPC客户端服务
/// </summary>
public partial class RpcService : IRpcService
{
    /// <summary>
    ///
    /// </summary>
    public RpcService()
    {
        string address = VampirewalCoreContext.GetInstance().GetOptionsValue<string>("ServerOptions:RpcServerAddress");

        client = new TcpTouchRpcClient();

        client.Setup(new TouchSocketConfig().SetRemoteIPHost(address)
                                                  .SetVerifyToken("VampirewalAdmin")
                                                  .SetVerifyTimeout(60000)
                                                  .SetRootPath(AppDomain.CurrentDomain.BaseDirectory)
                                                  .UsePlugin()
                                                  .ConfigurePlugins(a =>
                                                  {
                                                      a.UseReconnection(-1, false, 1000, (c) =>
                                                      {
                                                      });

                                                      a.Add<FilePlugin>();
                                                  })
                                                  .ConfigureRpcStore(a =>
                                                  {
                                                      a.RegisterServer<ReverseCallbackServer>();
                                                  }));

        client.Connect();
    }

    public TcpTouchRpcClient client { get; private set; }

    public void CallApi(string MethodName, InvokeOption option, Action<Exception> Error, params object[] parameters)
    {
        try
        {
            client.Invoke(MethodName, option, parameters);
        }
        catch (Exception ex)
        {
            Error.Invoke(ex);
        }
    }

    public void CallApi<T>(string MethodName, InvokeOption option, Action<T> success, Action<Exception>? Error = null, params object[] parameters)
    {
        try
        {
            var response = client.Invoke<T>(MethodName, option, parameters);

            success.Invoke(response);
        }
        catch (Exception ex)
        {
            Error?.Invoke(ex);
        }
    }

    public void CallApiByJson<T>(string MethodName, InvokeOption option, Action<Exception> Error, T? t = null) where T : class
    {
        try
        {
            string json = JsonConvert.SerializeObject(t);

            client.Invoke(MethodName, option, json);
        }
        catch (Exception ex)
        {
            Error.Invoke(ex);
        }
    }

    public void CallApiByJson<T>(string MethodName, InvokeOption option, Action<T> success, Action<Exception>? Error = null, T? t = null) where T : class
    {
        try
        {
            string json = JsonConvert.SerializeObject(t);

            var response = client.Invoke<ResponseBasic>(MethodName, option, json);

            if (response.IsSuccess)
            {
                var model = JsonConvert.DeserializeObject<T>(response.Data);
                success.Invoke(model);
            }
        }
        catch (Exception ex)
        {
            Error?.Invoke(ex);
        }
    }

    public async Task<bool> FileDownload(string FileServerName, string SavePath = "")
    {
        Metadata metadata = new Metadata();//传递到服务器的元数据
        metadata.Add("Operation", "下载附件");
        //metadata.Add("BillId", BillId);
        //metadata.Add("UserId", BD_SystemDataContext.GetInstance.BDLoginUserInfo.BillId);
        //metadata.Add("UserName", BD_SystemDataContext.GetInstance.BDLoginUserInfo.name);
        //metadata.Add("Type", Convert.ToInt32(attachmentType).ToString());
        //metadata.Add("FileSize", fileInfo.Length.ToString());
        //metadata.Add("FileName", FileName);

        FileOperator fileOperator = new FileOperator()
        {
            Flags = TransferFlags.BreakpointResume,
            SavePath = $@"Windows.iso",//保存路径
            ResourcePath = @"D:\System\Windows.iso",//请求路径
            Metadata = metadata//传递到服务器的元数据
        };//实例化本次传输的控制器，用于获取传输进度、速度、状态等。

        fileOperator.Timeout = TimeSpan.FromSeconds(60);

        //此处的作用相当于Timer，定时每秒输出当前的传输进度和速度。
        TouchSocket.Core.LoopAction loopAction = TouchSocket.Core.LoopAction.CreateLoopAction(-1, 1000, (loop) =>
        {
            if (fileOperator.Result.ResultCode != ResultCode.Default)
            {
                loop.Dispose();
            }

            //client.Logger.Info($"进度：{fileOperator.Progress}，速度：{fileOperator.Speed()}");
        });

        await loopAction.RunAsync();

        //var aaa =

        //此方法会阻塞，直到传输结束，也可以使用PullFileAsync
        IResult result = await client.PullFileAsync(fileOperator);

        if (result.ResultCode == ResultCode.Success)
        {
            return true;
        }

        return false;
    }

    public async Task<bool> FileUpload(string BillId, string FilePath, string FileName)
    {
        return await Task<bool>.Run(async () =>
        {
            FileInfo fileInfo = new FileInfo(FilePath);

            Metadata metadata = new Metadata();//传递到服务器的元数据
            metadata.Add("Operation", "上传附件");
            metadata.Add("BillId", BillId);
            metadata.Add("UserId", VampirewalCoreContext.GetInstance().GetContext<string>("UserId"));
            metadata.Add("UserName", VampirewalCoreContext.GetInstance().GetContext<string>("UserName"));
            metadata.Add("FileSize", fileInfo.Length.ToString());
            metadata.Add("FileName", FileName);

            FileOperator fileOperator = new FileOperator()
            {
                Flags = TransferFlags.BreakpointResume,//尝试断点续传，使用断点续传时，会验证MD5值
                SavePath = $@"{DateTime.Today}\{fileInfo.Name}",//保存路径
                ResourcePath = FilePath,//请求路径
                Metadata = metadata//传递到服务器的元数据
            };//实例化本次传输的控制器，用于获取传输进度、速度、状态等。

            fileOperator.Timeout = TimeSpan.FromSeconds(60);

            //此处的作用相当于Timer，定时每秒输出当前的传输进度和速度。
            TouchSocket.Core.LoopAction loopAction = TouchSocket.Core.LoopAction.CreateLoopAction(-1, 1000, (loop) =>
            {
                if (fileOperator.Result.ResultCode != ResultCode.Default)
                {
                    loop.Dispose();
                }

                //client.Logger.Info($"进度：{fileOperator.Progress}，速度：{fileOperator.Speed()}");
            });

            await loopAction.RunAsync();

            //var aaa =

            //此方法会阻塞，直到传输结束，也可以使用PullFileAsync
            IResult result = await client.PushFileAsync(fileOperator);

            if (result.ResultCode == ResultCode.Success)
            {
                return true;
            }

            return false;
        });
    }

    public void ResponseApi<T>(string MethodName, Action<T> success, Action<Exception> Error = null, params object[] parameters)
    {
        try
        {
            ResponseBasic response = client.Invoke<ResponseBasic>(MethodName, InvokeOption.WaitInvoke, parameters);

            if (response.IsSuccess)
            {
                var current = JsonConvert.DeserializeObject<T>(response.Data);

                success.Invoke(current);
            }
            else
            {
                throw new Exception(response.Message);
            }
        }
        catch (Exception ex)
        {
            Error?.Invoke(ex);
        }
    }

    public void ResponseApiByJson<T>(object obj, string MethodName, Action<T> success, Action<Exception> Error = null)
    {
        try
        {
            string json = JsonConvert.SerializeObject(obj);

            ResponseBasic response = client.Invoke<ResponseBasic>(MethodName, InvokeOption.WaitInvoke, json);

            if (response.IsSuccess)
            {
                var current = JsonConvert.DeserializeObject<T>(response.Data);

                success.Invoke(current);
            }
            else
            {
                throw new Exception(response.Message);
            }
        }
        catch (Exception ex)
        {
            Error?.Invoke(ex);
        }
    }
}

public class ResponseBasic
{
    public int Code { get; set; }

    public string Message { get; set; }

    public string Data { get; set; }

    public bool IsSuccess { get; set; }
}